/*!	 synfig/rendering/software/function/packedsurface.h
**	 PackedSurface Header
**
**	......... ... 2016 Ivan Mahonin
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifndef __SYNFIG_RENDERING_SOFTWARE_PACKEDSURFACE_H
#define __SYNFIG_RENDERING_SOFTWARE_PACKEDSURFACE_H

#include <set>

#include <synfig/real.h>
#include <synfig/color.h>
#include <synfig/mutex.h>
#include <synfig/surface.h>

#include "../../primitive/contour.h"
#include "../../primitive/polyspan.h"

namespace synfig
{
namespace rendering
{
namespace software
{

class PackedSurface
{
public:
    enum ChannelType {
        ChannelUInt8,
        ChannelFloat32
    };

    enum {
        ChunkSize = 32,
        CacheRows = 2
    };

    class Reader
    {
    private:
        struct CacheEntry {
            int chunk_index;
            CacheEntry *prev;
            CacheEntry *next;
            void *data(int offset = 0)
            {
                return (char*)this + sizeof(*this) + offset;
            }
        };

        const PackedSurface *surface;
        mutable CacheEntry* first;
        mutable CacheEntry* last;
        mutable std::vector<CacheEntry*> chunks;
        char* cache;

    public:

        Reader();
        explicit Reader(const PackedSurface &surface);
        ~Reader();

        void open(const PackedSurface &surface);
        void close();
        bool is_opened() const
        {
            return surface != NULL;
        }

        Color get_pixel(int x, int y) const;

        inline static Color reader(const void *surf, int x, int y)
        {
            return ((const Reader*)surf)->get_pixel(x, y);
        }
        inline static ColorAccumulator reader_cook(const void *surf, int x, int y)
        {
            return ColorPrep::cook_static(reader(surf, x, y));
        }
    };

    struct DiscreteHelper {
        Color::value_type value;
        Color::value_type min;
        Color::value_type max;
        int count;
        DiscreteHelper(): value(), min(), max(), count() { }
        DiscreteHelper(Color::value_type c):
            value(c),
            min(c - real_low_precision<Color::value_type>()),
            max(c + real_low_precision<Color::value_type>()),
            count(1)
        { }
        bool in_range(Color::value_type c) const
        {
            return min <= c && c <= max;
        }
    };

private:
    mutable synfig::Mutex mutex;
    mutable std::set<Reader*> readers;

    int width;
    int height;

    ChannelType channel_type;
    int channels[4];
    Color::value_type discrete_to_float[256];
    Color constant;

    int pixel_size;
    int row_size;

    int chunk_size;
    int chunk_row_size;
    int chunks_width;
    int chunks_height;

    std::vector<char> data;

    static Color::value_type get_channel(const void *pixel, int offset, ChannelType type, Color::value_type constant, const Color::value_type *discrete_to_float);
    static void set_channel(void *pixel, int offset, ChannelType type, Color::value_type color, const Color::value_type *discrete_to_float);

    Color get_pixel(const void *pixel) const;
    void set_pixel(void *pixel, const Color &color);

    void get_compressed_chunk(int index, const void *&data, int &size, bool &compressed) const;

public:
    PackedSurface();
    ~PackedSurface();

    void clear();
    void set_pixels(const Color *pixels, int width, int height, int pitch = 0);
    int get_width() const
    {
        return width;
    }
    int get_height() const
    {
        return height;
    }
    void get_pixels(Color *target) const;
};

} /* end namespace software */
} /* end namespace rendering */
} /* end namespace synfig */

#endif